﻿#ifndef CUSTOM_BRDF_INCLUDED
#define CUSTOM_BRDF_INCLUDED

struct BRDF {
	float3 diffuse;
	float3 specular;
	float roughness;
	float perceptualRoughness;
	float fresnel;
};

#define MIN_REFLECTIVITY 0.04

#if _SKIN	
float3 DiffuseScattering(Surface surface, BRDF brdf, Light light)
{
	half curvature = surface.curvature;
	half thickness = 1 - surface.thickness;

	half dotNL = max(0, dot(surface.normal, light.direction));
	//	Skin Lighting
	float4 brdfUV = float4(0, 0, 0, 0);
		// Half-Lambert lighting value based on blurred normals.
	brdfUV.x = dotNL * 0.5 + 0.5;
		// Curvature amount. Multiplied by light's luminosity so brighter light = more scattering.
		// Pleae note: gi.light.color already contains light attenuation
	brdfUV.y = curvature * dot(light.color, half3(0.22, 0.707, 0.071));
	
	//half3 brdf = tex2D(_BRDFTex, brdfUV).rgb;

	float3 brdfData = GetBRDFData(brdfUV).rgb;
	return lerp(dotNL.xxx, brdfData, thickness);
}

float3 LightScattering(Surface surface, BRDF brdf, Light light)
{
	half thickness = 1-surface.thickness;
	//	Translucency
	//	#if defined (DIRECTIONAL) && defined (LUX_DIRECTIONAL_SSS) || defined (DIRECTIONAL_COOKIE) && defined (LUX_DIRECTIONAL_SSS) || defined (POINT) && defined (LUX_POINT_SSS) || defined (POINT_COOKIE) && defined (LUX_POINT_SSS) || defined (SPOT) && defined (LUX_SPOT_SSS)
	float3 H = normalize(light.direction + surface.normal * surface.distortion);
	float transDot = pow(saturate(dot(surface.viewDirection, -H)), surface.power) * surface.thicknessScale*thickness;
	float3 lightScattering = transDot * surface.subColor;
	return lightScattering;
	//return float3(0, 0, 0);
}
#elif _FABRIC
inline float FabricD(float NdotH, float roughness)
{
	return 0.96 * pow(1 - NdotH, 2) + 0.057;
}

inline half FabricScatterFresnelLerp(half nv, half scale)
{
	half t0 = Pow4(1 - nv);
	half t1 = 0.4 * (1 - nv);
	return (t1 - t0) * scale + t0;
}

float3 FabricScattering(Surface surface, BRDF brdf, Light light)
{
	half nl = saturate(dot(surface.normal, light.direction));
	half nv = abs(dot(surface.normal, surface.viewDirection)); // This abs allow to limit artifact
	return surface.fabricScatterColor * (nl * 0.5 + 0.5) * FabricScatterFresnelLerp(nv, surface.fabricScatterScale);
}

#endif

float OneMinusReflectivity (float metallic) {
	float range = 1.0 - MIN_REFLECTIVITY;
	return range - metallic * range;
}

BRDF GetBRDF (Surface surface, bool applyAlphaToDiffuse = false) {
	BRDF brdf;
	float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);

	brdf.diffuse = surface.color * oneMinusReflectivity;
	if (applyAlphaToDiffuse) {
		brdf.diffuse *= surface.alpha;
	}
	brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);

	brdf.perceptualRoughness =
		PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);
	brdf.roughness = PerceptualRoughnessToRoughness(brdf.perceptualRoughness);
	
	brdf.fresnel = saturate(surface.smoothness + 1.0 - oneMinusReflectivity);
	return brdf;
}

float SpecularStrength (Surface surface, BRDF brdf, Light light) {
	float3 h = SafeNormalize(light.direction + surface.viewDirection);
	float nh = saturate(dot(surface.normal, h));
	float nh2 = Square(nh);
	float lh2 = Square(saturate(dot(light.direction, h)));
	float r2 = Square(brdf.roughness);
	float d2 = Square(nh2 * (r2 - 1.0) + 1.00001);
	float normalization = brdf.roughness * 4.0 + 2.0;
#if _FABRIC
	float ss = brdf.roughness > 0.99 ? 1 * FabricD(nh, brdf.roughness) : r2 / (d2 * max(0.1, lh2) * normalization);
	return ss;
#else
	return r2 / (d2 * max(0.1, lh2) * normalization);
#endif
}

float3 DirectBRDF (Surface surface, BRDF brdf, Light light) {
#if _SKIN	
	return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse*DiffuseScattering(surface, brdf, light);
	//return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
#else
	return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
#endif
}

float3 IndirectBRDF (
	Surface surface, BRDF brdf, float3 diffuse, float3 specular
) {
	float fresnelStrength = surface.fresnelStrength *
		Pow4(1.0 - saturate(dot(surface.normal, surface.viewDirection)));
	
#if _FABRIC
	float3 reflection = float3(0, 0, 0);
#else
	float3 reflection =
		specular * lerp(brdf.specular, brdf.fresnel, fresnelStrength);
	reflection /= brdf.roughness * brdf.roughness + 1.0;
#endif
		
    return (diffuse * brdf.diffuse + reflection) * surface.occlusion;
}

#endif